﻿' ******************************************************************************
' ** 
' **  Yahoo! Managed
' **  Written by Marius Häusler 2011
' **  It would be pleasant, if you contact me when you are using this code.
' **  Contact: YahooFinanceManaged@gmail.com
' **  Project Home: http://code.google.com/p/yahoo-finance-managed/
' **  
' ******************************************************************************
' **  
' **  Copyright 2011 Marius Häusler
' **  
' **  Licensed under the Apache License, Version 2.0 (the "License");
' **  you may not use this file except in compliance with the License.
' **  You may obtain a copy of the License at
' **  
' **    http://www.apache.org/licenses/LICENSE-2.0
' **  
' **  Unless required by applicable law or agreed to in writing, software
' **  distributed under the License is distributed on an "AS IS" BASIS,
' **  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
' **  See the License for the specific language governing permissions and
' **  limitations under the License.
' ** 
' ******************************************************************************


Namespace YahooManaged.Finance.Support

    ''' <summary>
    ''' Class for downloading and calculating exchange rates
    ''' </summary>
    ''' <remarks></remarks>
    Public Class ExchangeRateCalculator
        ''' <summary>
        ''' Raises when started asynchronous download completes
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <remarks></remarks>
        Public Event AsyncUpdateCompleted(ByVal sender As ExchangeRateCalculator, ByVal ea As ExchangeRateCalculatorCompletedEventArgs)

        Private ReadOnly mHelper As New MyHelper
        Private mExchangeItems(-1) As ExchangeRateData
        Private WithEvents mDownloader As New ExchangeRateDownload
        Private mDonwloadCounter As Long = 0

        ''' <summary>
        ''' The downloaded or setted exchange rate informations
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>By setting the items the base currency of each ExchangeRateData must be the same</remarks>
        Public Property ExchangeItems() As ExchangeRateData()
            Get
                Return mExchangeItems
            End Get
            Set(ByVal value As ExchangeRateData())
                If value IsNot Nothing AndAlso value.Length > 0 Then
                    If value(0).CurrencyRelation.BaseCurrency IsNot Nothing Then
                        Dim bc As CurrencyInfo = value(0).CurrencyRelation.BaseCurrency
                        Dim hasSameBC As Boolean = True
                        For Each item As ExchangeRateData In value
                            If item.CurrencyRelation.BaseCurrency Is Nothing OrElse item.CurrencyRelation.BaseCurrency.ID <> bc.ID Then
                                hasSameBC = False
                                Exit For
                            End If
                        Next
                        If hasSameBC Then mExchangeItems = value
                    End If
                End If
            End Set
        End Property
        ''' <summary>
        ''' The used proxy informations
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Proxy() As Net.IWebProxy
            Get
                Return mDownloader.Proxy
            End Get
            Set(ByVal value As Net.IWebProxy)
                mDownloader.Proxy = value
            End Set
        End Property

        ''' <summary>
        ''' Updates exchange informations
        ''' </summary>
        ''' <param name="baseCurrency">The currency all other currencies are depending</param>
        ''' <param name="currencies">The dependent currencies</param>
        ''' <remarks></remarks>
        Public Sub Update(ByVal baseCurrency As CurrencyInfo, ByVal currencies As IEnumerable(Of CurrencyInfo))
            mDownloader.CancelAsyncAll()
            mDonwloadCounter += 1
            mExchangeItems = New ExchangeRateData() {}
            Dim resp As ExchangeRateResponse = mDownloader.Download(Me.GetCurrencyList(baseCurrency, currencies))
            If resp.Connection.State = Base.ConnectionState.Success Then mExchangeItems = resp.Result
        End Sub
        ''' <summary>
        ''' Starts asynchronous update of exchange rates
        ''' </summary>
        ''' <param name="baseCurrency">The currency all other currencies are depending</param>
        ''' <param name="currencies">The dependent currencies</param>
        ''' <remarks></remarks>
        Public Sub UpdateAsync(ByVal baseCurrency As CurrencyInfo, ByVal currencies As IEnumerable(Of CurrencyInfo), Optional ByVal userArgs As Object = Nothing)
            mDownloader.CancelAsyncAll()
            mDonwloadCounter += 1
            mExchangeItems = New ExchangeRateData() {}
            mDownloader.DownloadAsync(Me.GetCurrencyList(baseCurrency, currencies), New AsyncDownloadArgs(userArgs, mDonwloadCounter))
        End Sub
        Public Sub UpdateAsync(ByVal baseCurrency As CurrencyInfo, ByVal currencies As IEnumerable(Of CurrencyInfo), ByVal tradeDate As DateTime, Optional ByVal userArgs As Object = Nothing)
            mDownloader.CancelAsyncAll()
            mDonwloadCounter += 1
            mExchangeItems = New ExchangeRateData() {}
            mDownloader.DownloadAsync(Me.GetCurrencyList(baseCurrency, currencies), tradeDate, New AsyncDownloadArgs(userArgs, mDonwloadCounter))
        End Sub

        ''' <summary>
        ''' Default constructor
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub New()
        End Sub

        Private Sub AsyncDownload_Completed(ByVal sender As Object, ByVal e As ExchangeRateDownloadCompletedEventArgs) Handles mDownloader.AsyncRateDownloadCompleted
            Dim dlArgs As AsyncDownloadArgs = DirectCast(e.UserArgs, AsyncDownloadArgs)
            If e.Response.Connection.State = Base.ConnectionState.Success And dlArgs.Counter = mDonwloadCounter Then mExchangeItems = e.Response.Result
            RaiseEvent AsyncUpdateCompleted(Me, New ExchangeRateCalculatorCompletedEventArgs(dlArgs.UserArgs, e.Response.Connection.State = Base.ConnectionState.Success))
        End Sub


        ''' <summary>
        ''' Converts as value of a currency to the ratio value of another currency
        ''' </summary>
        ''' <param name="value"></param>
        ''' <param name="currencyOfValue"></param>
        ''' <param name="returnCurrency"></param>
        ''' <returns></returns>
        ''' <remarks>Returns 0, if the dependency value of one of both currencies is not in the list</remarks>
        Public Function ConvertCurrency(ByVal value As Double, ByVal currencyOfValue As CurrencyInfo, ByVal returnCurrency As CurrencyInfo) As Double
            Try
                If currencyOfValue.ID <> returnCurrency.ID Then
                    Dim fromRatio As Double = 0
                    Dim toRatio As Double = 0
                    For Each eiFrom As ExchangeRateData In mExchangeItems
                        If eiFrom.CurrencyRelation.DepCurrency.ID = currencyOfValue.ID Then
                            fromRatio = eiFrom.DependencyValue
                            For Each eiTo As ExchangeRateData In mExchangeItems
                                If eiTo.CurrencyRelation.DepCurrency.ID = returnCurrency.ID Then
                                    toRatio = eiTo.DependencyValue
                                    Exit For
                                End If
                            Next
                            Exit For
                        End If
                    Next
                    If fromRatio <> 0 And toRatio <> 0 Then Return (value / fromRatio * toRatio)
                Else
                    Return value
                End If
            Catch ex As Exception
            End Try
            Return 0
        End Function

        Private Function GetCurrencyList(ByVal baseCurrency As CurrencyInfo, ByVal currencies As IEnumerable(Of CurrencyInfo)) As YCurrencyID()
            Dim lst As New List(Of YCurrencyID)
            Dim isInList As Boolean = False
            If currencies IsNot Nothing Then
                For Each cur As CurrencyInfo In currencies
                    lst.Add(New YCurrencyID(baseCurrency, cur))
                Next
            End If
            Return lst.ToArray
        End Function

        ''' <summary>
        ''' Converts as value of a currenciy to the ratio value of another currency; both ExchangeRataData have same base currency
        ''' </summary>
        ''' <param name="value">The value to convert</param>
        ''' <param name="currencyOfValue">The base currency</param>
        ''' <param name="returnCurrency">The dependent currency</param>
        ''' <returns></returns>
        ''' <remarks>Returns 0, if the base currency of both is not equal</remarks>
        Public Shared Function ConvertCurrency(ByVal value As Double, ByVal currencyOfValue As ExchangeRateData, ByVal returnCurrency As ExchangeRateData) As Double
            Try
                If currencyOfValue.CurrencyRelation.BaseCurrency.ID = returnCurrency.CurrencyRelation.BaseCurrency.ID Then
                    If currencyOfValue.CurrencyRelation.DepCurrency.ID <> returnCurrency.CurrencyRelation.DepCurrency.ID Then
                        Dim fromRatio As Double = currencyOfValue.DependencyValue
                        Dim toRatio As Double = returnCurrency.DependencyValue
                        If fromRatio <> 0 And toRatio <> 0 Then : Return (value / fromRatio * toRatio)
                        Else : Return 0
                        End If
                    Else
                        Return value
                    End If
                Else
                    Throw New ArgumentException("The exchange rates have not the same base currency.")
                End If
            Catch ex As Exception
                Return 0
            End Try
        End Function


        Public Shared Function ConvertCurrency(ByVal value As Double, ByVal exchangeRate As ExchangeRateData, Optional ByVal reverse As Boolean = False) As Double
            If exchangeRate IsNot Nothing AndAlso exchangeRate.LastTradePriceOnly <> 0 Then
                If Not reverse Then
                    Return value * exchangeRate.LastTradePriceOnly
                Else
                    Return value / exchangeRate.LastTradePriceOnly
                End If
            Else
                Throw New ArgumentException("The exchange rate is invalid", "exchangeRate")
            End If
        End Function

        ''' <summary>
        ''' Returns a data table where every available currency is value converted to each other
        ''' </summary>
        ''' <param name="firstColumnName">The text in the left edge of the column header</param>
        ''' <param name="items">The exchange items. Every exchange item must have the same base currency.</param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Shared Function CrossDataTable(ByVal items As IEnumerable(Of Support.ExchangeRateData), Optional ByVal firstColumnName As String = "") As System.Data.DataTable
            Dim dt As New System.Data.DataTable
            If items IsNot Nothing Then
                dt.Columns.Add(firstColumnName, GetType(String))
                For Each curX As Support.ExchangeRateData In items
                    If curX IsNot Nothing Then dt.Columns.Add(curX.CurrencyRelation.DepCurrency.ID, GetType(Double))
                Next
                For Each curY As Support.ExchangeRateData In items
                    If curY IsNot Nothing Then
                        Dim row As System.Data.DataRow = dt.NewRow
                        row(0) = curY.CurrencyRelation.DepCurrency.ID
                        For Each curX As Support.ExchangeRateData In items
                            If curX IsNot Nothing Then row(curX.CurrencyRelation.DepCurrency.id) = Math.Round(Support.ExchangeRateCalculator.ConvertCurrency(1, curY, curX), 4)
                        Next
                        dt.Rows.Add(row)
                    End If
                Next
            End If
            Return dt
        End Function

        Private Class AsyncDownloadArgs
            Inherits Base.DownloadEventArgs
            Public Counter As Long = 0
            Public Sub New(ByVal userArgs As Object, ByVal cnt As Long)
                MyBase.New(userArgs)
                Me.Counter = cnt
            End Sub
        End Class
    End Class

    ''' <summary>
    ''' Stores the downloaded size in bytes
    ''' </summary>
    ''' <remarks></remarks>
    Public Class ExchangeRateCalculatorCompletedEventArgs
        Inherits Base.DownloadEventArgs
        Private mSuccess As Boolean
        Public ReadOnly Property Success() As Boolean
            Get
                Return mSuccess
            End Get
        End Property
        Friend Sub New(ByVal userArgs As Object, ByVal success As Boolean)
            MyBase.New(userArgs)
            mSuccess = success
        End Sub
    End Class

End Namespace